using System;
using System.Activities;
using System.Activities.Statements;
using System.Collections.Generic;
using System.Linq;
using Headsight.BPMN2WF.BPMN.Model;
using Headsight.BPMN2WF.BPMN.Schemas;

namespace Headsight.BPMN2WF.BPMN.Utils
{
    /// <summary>
    /// Diversified extension methods.
    /// </summary>
    public static class Extensions
    {
        /// <summary>
        /// <see cref="List{T}.ForEach"/> for <see cref="IEnumerable{T}"/>.
        /// </summary>
        public static void ForEach<T>(this IEnumerable<T> self, Action<T> action)
        {
            foreach (var each in self)
            {
                action(each);
            }
        }

        /// <summary>
        /// Returns self as <see cref="FlowNode"/>. If <para>self</para> is already an instance of 
        /// <see cref="FlowNode"/> then self casted to <see cref="FlowNode"/> is returned. Else
        /// <para>self</para> is wrapped into an instance of <see cref="FlowStep"/>.
        /// Having this extension method on <see cref="Object"/> is for sure not that nice but
        /// works for now. 
        /// </summary>
        public static FlowStep ToFlowStep(this Object self)
        {
            if (self is FlowStep)
            {
                return (FlowStep)self;
            }

            if (self is Flowchart)
            {
                return new FlowStep { Action = (Flowchart)self };
            }

            return new FlowStep { Action = (Activity)self };
        }

        /// <summary>
        /// Temporary: Returns the <i>expression</i> of the <see cref="BPMNSequenceFlow"/> respectively its
        /// <see cref="BPMNBaseElement{T}.BPMN2WF.BPMN.Schemas.tSequenceFlow}.TElement"/>
        /// <i>flowing to the target <see cref="BPMNFlowNode"/></i> <para>flowNode</para>.
        /// 
        /// This is a temporary works for now implementation...
        /// </summary>
        public static Tuple<String, String> GetExpressionIdAndExpression(this BPMNFlowNodesBlock process, BPMNFlowNode flowNodeGateway, BPMNFlowNode flowNodeBranch)
        {
            // not really save... there are potentially multiple incoming flows... but works for now ;-)
            var seqFlow = process.BPMNSequenceFlows.First(x =>
                                                             (x.TElement.sourceRef == flowNodeGateway.Id) &&
                                                             (x.TElement.targetRef == flowNodeBranch.Id));


            var expression = "<empty conditionExpression>";

            if (seqFlow.TElement.conditionExpression != null && seqFlow.TElement.conditionExpression.Text != null)
            {
                var exp = String.Join(String.Empty, seqFlow.TElement.conditionExpression.Text).Replace("\n", "").Replace("\r", "");

                expression = (!String.IsNullOrEmpty(exp)) ? exp : expression;
            }

            var expressionId = (!String.IsNullOrEmpty(flowNodeBranch.TElement.name)) ? 
                String.Format("{0} (id: {1})", flowNodeBranch.TElement.name, flowNodeBranch.Id)
                : flowNodeBranch.Id;

            return new Tuple<String,String>(expressionId, expression);
        }

        /// <summary>
        /// Temporary: If <para>flowNode</para> represents a <see cref="tGateway"/> with a <i>default branch configuration</i>
        /// then <code>true</code> is returned else <code>false</code>.
        /// 
        /// This is a temporary works for now implementation...
        /// </summary>
        public static bool HasDefaultBranch(this BPMNFlowNodesBlock process, BPMNFlowNode bpmnFlowNode)
        {
            return GetDefaultBranch(process, bpmnFlowNode) != null;
        }

        /// <summary>
        /// Temporary: Returns <para>true</para> if <see cref="GetDefaultBranch"/>'s result in case of 
        /// <para>flowNodeGateway</para> is equal to <para>flowNode</para> else <code>false</code>.
        /// </summary>
        public static bool IsDefaultBranchOf(this BPMNFlowNodesBlock process, BPMNFlowNode flowNodeGateway, BPMNFlowNode flowNode)
        {
            return flowNode.Equals(GetDefaultBranch(process, flowNodeGateway));
        }

        /// <summary>
        /// Temporary: If <para>flowNode</para> represents a <see cref="tGateway"/> with a <i>default branch configuration</i>
        /// then the <see cref="BPMNFlowNode"/> representing the target of the <i>default branch</i> is returned else <i>undefined behavior</i>.
        /// 
        /// This is a temporary works for now implementation...
        /// </summary>
        public static BPMNFlowNode GetDefaultBranch(this BPMNFlowNodesBlock process, BPMNFlowNode bpmnFlowNode)
        {
            if (bpmnFlowNode.TElement is tInclusiveGateway || bpmnFlowNode.TElement is tExclusiveGateway || bpmnFlowNode.TElement is tComplexGateway)
            {
                dynamic tGateway = bpmnFlowNode.TElement;
                var idOfDefaultSeqFlow = (String)tGateway.@default;

                if (String.IsNullOrEmpty(idOfDefaultSeqFlow))
                {
                    return null;
                }

                var seqFlow = process.GetBPMNSequenceFlowById(idOfDefaultSeqFlow);

                return process.GetBPMNFlowNodeById(seqFlow.TElement.targetRef);
            }

            return null;
        }

        /// <summary>
        /// Temporary: If <para>flowNode</para> is an instance of <see cref="BPMNFlowNodesBlock"/> then 
        /// <see cref="BPMNFlowNodesBlock.NodeIn"/> is returned else <para>flowNode</para> itself.
        /// </summary>
        public static BPMNFlowNode BPMNFlowNodeOrNodeIn(this BPMNFlowNode flowNode)
        {
            if (flowNode is BPMNFlowNodesBlock)
            {
                return ((BPMNFlowNodesBlock) flowNode).FlowNodeIn;
            }

            return flowNode;
        }
    }
}